package core

import (
	"bytes"
	"crypto/sha256"
	"encoding/gob"
	"log"
	"strconv"
	"time"
)

//Block 区块结构体
type Block struct {
	Timestamp     int64  //区块创建的时间戳
	Data          []byte //当前区块存的有效信息，比如发送多少币给谁
	PrevBlockHash []byte //上一个区块的hash
	Hash          []byte //当前区块的hash
	Nonce         int    //用于装计数器结果数
}

//SetHash 方法，用于获取本区块hash
func (b *Block) SetHash() {
	//获取结构体的时间戳并转化为字节
	time := []byte(strconv.FormatInt(b.Timestamp, 10))
	//将上个区块的hash，本区块的数据以及本区块创建时间拼接为一个字节切片
	hearders := bytes.Join([][]byte{b.PrevBlockHash, b.Data, time}, []byte{})
	//最终将拼接好的结果计算sha256就得到了本区块hash
	hash := sha256.Sum256(hearders)
	b.Hash = hash[:]
}

//Serialize 序列化数据，将区块结构体转换为[]byte，用于数据库在数据库中作为value存储
func (b *Block) Serialize() []byte {
	//创建一个缓冲区用来存放数据
	var result bytes.Buffer
	//初始化一个编码器并将存储地址传进去，这里要求传入的结构体必须有Write方法
	encoder := gob.NewEncoder(&result)
	//将需要序列化的数据传入
	err := encoder.Encode(b)
	//出现错误就退出程序
	if err != nil {
		log.Panic(err)
	}
	//最后返回一个字节数组
	return result.Bytes()
}

//DeserializeBlock 解序列化数据，将数据库存储的[]byte转换为结构体
func DeserializeBlock(d []byte) *Block {
	//用来装解码后的数据
	var block Block
	//定义一个解码器，放入需要解码的数据
	decoder := gob.NewDecoder(bytes.NewReader(d))
	//将数据解码后放入
	err := decoder.Decode(&block)
	if err != nil {
		log.Panic(err)
	}
	return &block
}

//NewBlock 创建新区块
func NewBlock(data string, prevBlockHash []byte) *Block {
	//将传进来的数据装入结构体组成区块
	block := &Block{time.Now().Unix(), []byte(data), prevBlockHash, []byte{}, 0}
	//通过工作量证明获取hash和计数器结果
	pow := NewProofOfWork(block)
	nonce, hash := pow.Run()
	//设置hash和计数器结果，这里用工作量证明获得的hash取代了SetHash方法
	block.Hash = hash[:]
	//计数器是为了让其他人验证区块是否经过计算所得
	block.Nonce = nonce
	//最终返回一个区块
	return block
}

//NewGenesisBlock 创世块，区块链里的第一个区块
func NewGenesisBlock() *Block {
	//直接返回一个区块
	return NewBlock("Genesis Block", []byte{})
}
